package com.pine.app.module.security.oauth.provider.sso.token;

import com.pine.app.module.security.core.common.AuthConstant;
import com.pine.app.module.security.oauth.config.properties.OAuth2SsoProperties;
import com.pine.app.module.security.oauth.provider.AuthenticationManager;
import com.pine.app.module.security.oauth.provider.OAuth2Authentication;
import com.pine.app.module.security.oauth.provider.SsoOAuth2Authentication;
import com.pine.app.module.security.oauth.provider.token.OAuth2AccessToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

/**
 * 单点登陆认证处理器
 *
 * @author xiaoyuan
 * @create 2020/3/17 20:45
 **/
public abstract class AbstractOAuth2SsoTokenHandler implements OAuth2SsoTokenHandler {

    private RestOperations restTemplate = new RestTemplate();

    private AuthenticationManager authenticate;

    private OAuth2SsoProperties oAuth2ClientProperties;

    private final MultiValueMap<String, String> parameters = new LinkedMultiValueMap();

    protected  static final  String PINE_STATE = "PINE_SECURITY_STATE";


    public AbstractOAuth2SsoTokenHandler(OAuth2SsoProperties oAuth2ClientProperties) {
        ((RestTemplate) this.restTemplate).setErrorHandler(new DefaultResponseErrorHandler() {
            public void handleError(ClientHttpResponse response) throws IOException {
                if (response.getRawStatusCode() != 403) {
                    super.handleError(response);
                }
            }
        });
        this.oAuth2ClientProperties = oAuth2ClientProperties;
    }

    public AbstractOAuth2SsoTokenHandler(AuthenticationManager authenticate, OAuth2SsoProperties oAuth2ClientProperties) {
        ((RestTemplate) this.restTemplate).setErrorHandler(new DefaultResponseErrorHandler() {
            public void handleError(ClientHttpResponse response) throws IOException {
                if (response.getRawStatusCode() != 403) {
                    super.handleError(response);
                }
            }
        });
        this.authenticate = authenticate;
        this.oAuth2ClientProperties = oAuth2ClientProperties;
    }

    @Override
    public OAuth2Authentication getOAuth2Authentication(OAuth2AccessToken oAuth2AccessToken) {
        OAuth2Authentication oAuth2Authentication = authenticate.authentication(oAuth2AccessToken.getAccessToken());
        return new SsoOAuth2Authentication(oAuth2Authentication.getTokenRequest(), oAuth2Authentication.getUserDetails(), oAuth2AccessToken);
    }

    public Map<String, Object> postForMap(String path, MultiValueMap<String, String> formData, HttpHeaders headers) {
        if (headers.getContentType() == null) {
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        }
        Map map = this.restTemplate.exchange(path, HttpMethod.POST, new HttpEntity(formData, headers), Map.class, new Object[0]).getBody();
        return map;
    }

    public String buildUrl(String url, Map<String, String> params) {
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        Iterator iterator = params.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> param = (Map.Entry) iterator.next();
            builder.queryParam(param.getKey(), new Object[]{param.getValue()});
        }
        return builder.build().encode().toUriString();

    }

    public AuthenticationManager getAuthenticate() {
        return authenticate;
    }

    @Autowired
    public void setAuthenticate(AuthenticationManager authenticate) {
        this.authenticate = authenticate;
    }

    public OAuth2SsoProperties getoAuth2ClientProperties() {
        return oAuth2ClientProperties;
    }

    public void setoAuth2ClientProperties(OAuth2SsoProperties oAuth2ClientProperties) {
        this.oAuth2ClientProperties = oAuth2ClientProperties;
    }

    public void addState(String state) {
        this.parameters.add(AuthConstant.state, state);
    }

    public String getFirstState() {
        return this.parameters.getFirst(AuthConstant.state);
    }

    @Override
    public String getState() {
        return getFirstState();
    }
}
